import random
from collections import Counter
from enum import Enum


class CardSuit(Enum):
    """扑克牌花色"""
    SPADE = "♠"
    HEART = "♥"
    DIAMOND = "♦"
    CLUB = "♣"
    JOKER = "JOKER"


class CardRank(Enum):
    """扑克牌点数"""
    THREE = "3"
    FOUR = "4"
    FIVE = "5"
    SIX = "6"
    SEVEN = "7"
    EIGHT = "8"
    NINE = "9"
    TEN = "10"
    JACK = "J"
    QUEEN = "Q"
    KING = "K"
    ACE = "A"
    TWO = "2"
    LITTLE_JOKER = "LJ"
    BIG_JOKER = "BJ"


class Card:
    """扑克牌类"""

    def __init__(self, suit, rank):
        self.suit = suit
        self.rank = rank

    def __str__(self):
        if self.suit == CardSuit.JOKER:
            return f"{self.rank.value}"
        return f"{self.suit.value}{self.rank.value}"

    def __repr__(self):
        return str(self)

    def __eq__(self, other):
        """重载等号运算符，便于比较"""
        if not isinstance(other, Card):
            return False
        return self.suit == other.suit and self.rank == other.rank

    def __hash__(self):
        """使Card对象可哈希，便于在集合中使用"""
        return hash((self.suit, self.rank))

    def __lt__(self, other):
        # 定义牌的大小顺序
        order = [
            CardRank.THREE, CardRank.FOUR, CardRank.FIVE, CardRank.SIX, CardRank.SEVEN,
            CardRank.EIGHT, CardRank.NINE, CardRank.TEN, CardRank.JACK, CardRank.QUEEN,
            CardRank.KING, CardRank.ACE, CardRank.TWO, CardRank.LITTLE_JOKER, CardRank.BIG_JOKER
        ]
        return order.index(self.rank) < order.index(other.rank)


class Deck:
    """牌堆类"""

    def __init__(self):
        self.cards = []
        self.create_deck()

    def create_deck(self):
        """创建一副完整的牌"""
        # 普通牌
        for suit in [CardSuit.SPADE, CardSuit.HEART, CardSuit.DIAMOND, CardSuit.CLUB]:
            for rank in [
                CardRank.THREE, CardRank.FOUR, CardRank.FIVE, CardRank.SIX, CardRank.SEVEN,
                CardRank.EIGHT, CardRank.NINE, CardRank.TEN, CardRank.JACK, CardRank.QUEEN,
                CardRank.KING, CardRank.ACE, CardRank.TWO
            ]:
                self.cards.append(Card(suit, rank))

        # 大小王
        self.cards.append(Card(CardSuit.JOKER, CardRank.LITTLE_JOKER))
        self.cards.append(Card(CardSuit.JOKER, CardRank.BIG_JOKER))

    def shuffle(self):
        """洗牌"""
        random.shuffle(self.cards)

    def deal(self, num_players=3, cards_per_player=17):
        """发牌"""
        hands = [[] for _ in range(num_players)]

        for i in range(cards_per_player):
            for j in range(num_players):
                hands[j].append(self.cards.pop())

        # 底牌
        bottom_cards = self.cards
        self.cards = []

        return hands, bottom_cards


class HandType(Enum):
    """出牌类型"""
    PASS = 0  # 过
    SINGLE = 1  # 单张
    PAIR = 2  # 对子
    TRIPLE = 3  # 三张
    TRIPLE_WITH_ONE = 4  # 三带一
    TRIPLE_WITH_TWO = 5  # 三带二
    STRAIGHT = 6  # 顺子
    CONSECUTIVE_PAIRS = 7  # 连对
    AIRPLANE = 8  # 飞机
    AIRPLANE_WITH_WINGS = 9  # 飞机带翅膀
    FOUR_WITH_TWO = 10  # 四带二
    BOMB = 11  # 炸弹
    ROCKET = 12  # 火箭(王炸)


class Player:
    """玩家类"""

    def __init__(self, name, is_human=False):
        self.name = name
        self.hand = []
        self.is_landlord = False
        self.is_human = is_human

    def __str__(self):
        return f"{self.name}{'(地主)' if self.is_landlord else ''}"

    def sort_hand(self):
        """整理手牌"""
        self.hand.sort()

    def play_cards(self, cards):
        """出牌"""
        for card in cards:
            if card in self.hand:
                self.hand.remove(card)
        return cards

    def get_hand_str(self):
        """获取手牌字符串（空格分隔）"""
        return " ".join(str(card) for card in self.hand)

    def can_play(self, cards, last_played=None):
        """检查是否可以出这些牌"""
        if not cards:
            return True  # 过牌总是允许的

        # 检查玩家是否拥有这些牌
        hand_copy = self.hand.copy()
        for card in cards:
            if card not in hand_copy:
                print(f"错误：玩家没有这张牌: {card}")
                return False
            hand_copy.remove(card)

        # 检查牌型是否有效
        hand_type, value = GameLogic.check_cards_type(cards)
        if hand_type == HandType.PASS:
            print("错误：无效的牌型")
            return False

        # 如果上一轮有出牌，需要比较大小
        if last_played:
            last_type, last_value = last_played
            if hand_type == HandType.ROCKET:
                return True  # 火箭可以打任何牌
            if hand_type == HandType.BOMB:
                if last_type == HandType.BOMB:
                    return value > last_value  # 炸弹比较
                return True  # 炸弹可以打非火箭的任何牌
            if hand_type != last_type:
                return False  # 牌型必须相同
            return value > last_value  # 同类型牌比较大小

        return True  # 如果没有上一轮出牌，总是可以出牌


class GameLogic:
    """游戏逻辑类"""

    @staticmethod
    def check_cards_type(cards):
        """检查牌型并返回牌型和比较值"""
        if not cards:
            return HandType.PASS, None

        num_cards = len(cards)
        cards.sort()
        ranks = [card.rank for card in cards]
        rank_count = Counter(ranks)

        # 火箭
        if num_cards == 2 and CardRank.BIG_JOKER in ranks and CardRank.LITTLE_JOKER in ranks:
            return HandType.ROCKET, 1000

        # 炸弹
        if num_cards == 4 and len(rank_count) == 1:
            return HandType.BOMB, GameLogic.get_rank_value(cards[0].rank)

        # 单张
        if num_cards == 1:
            return HandType.SINGLE, GameLogic.get_rank_value(cards[0].rank)

        # 对子
        if num_cards == 2 and len(rank_count) == 1:
            return HandType.PAIR, GameLogic.get_rank_value(cards[0].rank)

        # 三张
        if num_cards == 3 and len(rank_count) == 1:
            return HandType.TRIPLE, GameLogic.get_rank_value(cards[0].rank)

        # 三带一
        if num_cards == 4:
            # 修复：考虑不同花色的情况
            if len(rank_count) == 2 and 3 in rank_count.values():
                # 找到三张的牌点
                triple_rank = next(rank for rank, count in rank_count.items() if count == 3)
                return HandType.TRIPLE_WITH_ONE, GameLogic.get_rank_value(triple_rank)

        # 三带二
        if num_cards == 5:
            if len(rank_count) == 2 and max(rank_count.values()) == 3:
                triple_rank = max(rank_count, key=lambda r: rank_count[r])
                return HandType.TRIPLE_WITH_TWO, GameLogic.get_rank_value(triple_rank)

        # 顺子 (至少5张连续牌)
        if num_cards >= 5:
            if GameLogic.is_consecutive(ranks):
                return HandType.STRAIGHT, GameLogic.get_rank_value(cards[0].rank)

        # 连对 (至少3对连续对子)
        if num_cards >= 6 and num_cards % 2 == 0:
            if all(count == 2 for count in rank_count.values()):
                unique_ranks = sorted(set(ranks), key=lambda r: GameLogic.get_rank_value(r))
                if GameLogic.is_consecutive(unique_ranks):
                    return HandType.CONSECUTIVE_PAIRS, GameLogic.get_rank_value(unique_ranks[0])

        # 飞机 (两个或更多连续三张)
        if num_cards >= 6 and num_cards % 3 == 0:
            triple_ranks = [r for r, count in rank_count.items() if count == 3]
            triple_ranks.sort(key=lambda r: GameLogic.get_rank_value(r))
            if len(triple_ranks) >= 2 and GameLogic.is_consecutive(triple_ranks):
                return HandType.AIRPLANE, GameLogic.get_rank_value(triple_ranks[0])

        # 飞机带翅膀
        if num_cards >= 8:
            triple_ranks = [r for r, count in rank_count.items() if count == 3]
            triple_ranks.sort(key=lambda r: GameLogic.get_rank_value(r))

            if len(triple_ranks) >= 2 and GameLogic.is_consecutive(triple_ranks):
                wings = num_cards - len(triple_ranks) * 3
                if wings == len(triple_ranks) or wings == len(triple_ranks) * 2:
                    return HandType.AIRPLANE_WITH_WINGS, GameLogic.get_rank_value(triple_ranks[0])

        # 四带二
        if num_cards == 6:
            quad_ranks = [r for r, count in rank_count.items() if count == 4]
            if quad_ranks:
                return HandType.FOUR_WITH_TWO, GameLogic.get_rank_value(quad_ranks[0])

        return HandType.PASS, None  # 无效牌型

    @staticmethod
    def is_consecutive(ranks):
        """检查是否连续"""
        values = [GameLogic.get_rank_value(r) for r in ranks]
        values = sorted(set(values))  # 去重并排序
        # 不能包含2和王
        if any(v > 12 for v in values):
            return False

        # 检查是否连续
        for i in range(1, len(values)):
            if values[i] - values[i - 1] != 1:
                return False
        return True

    @staticmethod
    def get_rank_value(rank):
        """获取牌点数值"""
        rank_order = [
            CardRank.THREE, CardRank.FOUR, CardRank.FIVE, CardRank.SIX, CardRank.SEVEN,
            CardRank.EIGHT, CardRank.NINE, CardRank.TEN, CardRank.JACK, CardRank.QUEEN,
            CardRank.KING, CardRank.ACE, CardRank.TWO, CardRank.LITTLE_JOKER, CardRank.BIG_JOKER
        ]
        return rank_order.index(rank)


class Game:
    """游戏类"""

    def __init__(self, player_names=None):
        self.players = [
            Player(player_names[0] if player_names else "玩家1", is_human=True),
            Player(player_names[1] if player_names else "玩家2"),
            Player(player_names[2] if player_names else "玩家3")
        ]
        self.deck = Deck()
        self.landlord_index = -1
        self.current_player_index = 0
        self.last_played = None  # (牌型, 比较值)
        self.last_played_cards = []
        self.last_played_player = None
        self.game_over = False
        self.winner = None
        self.bottom_cards = []

    def start(self):
        """开始游戏"""
        print("===== 斗地主游戏开始 =====")

        # 洗牌和发牌
        self.deck.shuffle()
        hands, self.bottom_cards = self.deck.deal()

        for i, player in enumerate(self.players):
            player.hand = hands[i]
            player.sort_hand()
            print(f"{player.name} 的手牌: {player.get_hand_str()}")

        # 随机选择地主
        self.landlord_index = random.randint(0, 2)
        self.players[self.landlord_index].is_landlord = True
        self.players[self.landlord_index].hand.extend(self.bottom_cards)
        self.players[self.landlord_index].sort_hand()

        print(f"\n{self.players[self.landlord_index]} 成为地主!")
        print(f"底牌: {' '.join(str(card) for card in self.bottom_cards)}")

        # 地主先出牌
        self.current_player_index = self.landlord_index
        print(f"\n===== 游戏开始，{self.players[self.current_player_index]} 先出牌 =====")

    def next_player(self):
        """轮到下一个玩家"""
        self.current_player_index = (self.current_player_index + 1) % 3

    def play_round(self):
        """进行一轮出牌"""
        if self.game_over:
            return

        current_player = self.players[self.current_player_index]
        print(f"\n当前玩家: {current_player}")
        print(f"手牌: {current_player.get_hand_str()}")

        if self.last_played_player == current_player:
            # 如果上一轮是当前玩家出的牌，则重置出牌
            self.last_played = None
            self.last_played_cards = []
            print("--- 新一轮开始 ---")

        if current_player.is_human:
            # 人类玩家
            cards_input = input("请出牌(空格分隔, 如 '♠3 ♥4', 输入p或空表示过): ").strip()
            if cards_input.lower() in ['p', 'pass', '']:
                played_cards = []
            else:
                card_strs = cards_input.split()
                played_cards = []
                for card_str in card_strs:
                    # 解析扑克牌
                    if card_str == "BJ":
                        played_cards.append(Card(CardSuit.JOKER, CardRank.BIG_JOKER))
                    elif card_str == "LJ":
                        played_cards.append(Card(CardSuit.JOKER, CardRank.LITTLE_JOKER))
                    else:
                        # 处理10点的情况
                        if len(card_str) > 2 and card_str[1] == '1' and card_str[2] == '0':
                            suit_str = card_str[0]
                            rank_str = "10"
                        elif len(card_str) > 1:
                            suit_str = card_str[0]
                            rank_str = card_str[1:]
                        else:
                            suit_str = card_str
                            rank_str = ""

                        suit = None
                        for s in CardSuit:
                            if s.value == suit_str:
                                suit = s
                                break
                        rank = None
                        for r in CardRank:
                            if r.value == rank_str:
                                rank = r
                                break
                        if suit and rank:
                            played_cards.append(Card(suit, rank))
                        else:
                            print(f"无法解析: {card_str}")
        else:
            # AI玩家
            played_cards = self.ai_play(current_player)

        # 验证出牌
        if played_cards and current_player.can_play(played_cards, self.last_played):
            current_player.play_cards(played_cards)
            hand_type, value = GameLogic.check_cards_type(played_cards)
            self.last_played = (hand_type, value)
            self.last_played_cards = played_cards
            self.last_played_player = current_player
            print(f"{current_player} 出牌: {' '.join(str(card) for card in played_cards)} ({hand_type.name})")

            # 检查游戏是否结束
            if not current_player.hand:
                self.game_over = True
                self.winner = current_player
                print(f"\n===== 游戏结束！{current_player} 获胜！=====")
                return
        elif played_cards:
            print("出牌无效，请重新选择")
            return  # 不切换到下一个玩家
        else:
            # 过牌
            print(f"{current_player} 选择过牌")
            if self.last_played_player == current_player:
                self.last_played = None
                self.last_played_cards = []

        # 切换到下一个玩家
        self.next_player()

    def ai_play(self, player):
        """AI玩家出牌逻辑"""
        # 简单AI策略:
        # 1. 如果是首轮出牌，出最小的合法牌
        # 2. 否则，尝试出比上家大的最小牌
        # 3. 如果无法出牌，则过

        if self.last_played is None:
            # 首轮出牌，出最小单张
            return [min(player.hand)]

        hand_type, last_value = self.last_played

        # 尝试出同类型的牌
        possible_plays = []

        # 根据牌型生成可能的出牌
        if hand_type == HandType.SINGLE:
            possible_plays = [[card] for card in player.hand
                              if GameLogic.get_rank_value(card.rank) > last_value]

        elif hand_type == HandType.PAIR:
            rank_count = Counter(card.rank for card in player.hand)
            for rank, count in rank_count.items():
                if count >= 2 and GameLogic.get_rank_value(rank) > last_value:
                    # 收集所有该点数的牌
                    cards_of_rank = [card for card in player.hand if card.rank == rank]
                    # 取前两张组成对子
                    pair_cards = cards_of_rank[:2]
                    possible_plays.append(pair_cards)

        # 三张
        elif hand_type == HandType.TRIPLE:
            rank_count = Counter(card.rank for card in player.hand)
            for rank, count in rank_count.items():
                if count >= 3 and GameLogic.get_rank_value(rank) > last_value:
                    triple_cards = [card for card in player.hand if card.rank == rank][:3]
                    possible_plays.append(triple_cards)

        # 三带一
        elif hand_type == HandType.TRIPLE_WITH_ONE:
            # 找出所有可能的三张
            rank_count = Counter(card.rank for card in player.hand)
            triple_candidates = [r for r, count in rank_count.items() if count >= 3]

            for triple_rank in triple_candidates:
                if GameLogic.get_rank_value(triple_rank) > last_value:
                    # 获取三张牌
                    triple_cards = [card for card in player.hand if card.rank == triple_rank][:3]
                    # 获取一张其他牌
                    other_cards = [card for card in player.hand if card.rank != triple_rank]
                    if other_cards:
                        possible_plays.append(triple_cards + [other_cards[0]])

        # 三带二
        elif hand_type == HandType.TRIPLE_WITH_TWO:
            rank_count = Counter(card.rank for card in player.hand)
            triple_candidates = [r for r, count in rank_count.items() if count >= 3]

            for triple_rank in triple_candidates:
                if GameLogic.get_rank_value(triple_rank) > last_value:
                    triple_cards = [card for card in player.hand if card.rank == triple_rank][:3]
                    # 找出对子
                    pair_candidates = [r for r, count in rank_count.items()
                                       if r != triple_rank and count >= 2]
                    if pair_candidates:
                        pair_rank = min(pair_candidates, key=GameLogic.get_rank_value)
                        pair_cards = [card for card in player.hand if card.rank == pair_rank][:2]
                        possible_plays.append(triple_cards + pair_cards)

        # 其他牌型的处理类似，为简化省略...

        # 如果没有合适的牌，尝试出炸弹或火箭
        if not possible_plays:
            # 检查是否有火箭
            big_joker = Card(CardSuit.JOKER, CardRank.BIG_JOKER)
            little_joker = Card(CardSuit.JOKER, CardRank.LITTLE_JOKER)
            if big_joker in player.hand and little_joker in player.hand:
                return [big_joker, little_joker]

            # 检查是否有炸弹
            rank_count = Counter(card.rank for card in player.hand)
            for rank, count in rank_count.items():
                if count == 4 and GameLogic.get_rank_value(rank) > last_value:
                    bomb_cards = [card for card in player.hand if card.rank == rank][:4]
                    return bomb_cards

        # 如果有合适的牌，选择最小的牌
        if possible_plays:
            # 选择最小价值的牌
            return min(possible_plays, key=lambda cards: max(GameLogic.get_rank_value(c.rank) for c in cards))

        # 无法出牌
        return []

    def run(self):
        """运行游戏"""
        self.start()

        while not self.game_over:
            self.play_round()

        # 显示最终结果
        print("\n===== 游戏结果 =====")
        for player in self.players:
            print(f"{player}: {len(player.hand)}张牌")
        print(f"获胜者: {self.winner}")


# 运行游戏
if __name__ == "__main__":
    # 创建游戏实例并运行
    game = Game(["你", "AI玩家1", "AI玩家2"])
    game.run()